//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xexec.cpp
// AUTHOR:          Vladimir Gumenuk
// DATE:            7-Dec-2012
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "xruntime.h"
#ifdef BUILD_FOR_PC
#include <windows.h>
#elif defined (BUILD_FOR_MAC)
#include <sys/time.h>
#endif

static void __cdecl DumpClient(void *pPtr, size_t iSize )
{
    fprintf( stderr, "Leak: 0x%08X size: %zu\n", pPtr, iSize );
}

static int __cdecl CrtReportHook(int i, char *pMsg, int *piVal )
{
    fprintf( stderr, "Leak: i: %d %s, size: %d\n", i, pMsg, *piVal );
    return 0;
}

qword GetTimeMs64()
{
#ifdef WIN32
    FILETIME ft;
    LARGE_INTEGER li;
    //QueryPerformanceCounter( &li );
    // Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC)
    GetSystemTimeAsFileTime( &ft );
    li.LowPart = ft.dwLowDateTime;
    //li.HighPart = ft.dwHighDateTime;

    qword ret = li.QuadPart;
    ret -= 116444736000000000LL; // Convert from file time to UNIX epoch time.
    ret /= 10000; // From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals
#else // Mac OS, Linux
    struct timeval tv;
    gettimeofday(&tv, NULL);
    qword ret = tv.tv_usec;
    // Convert from micro seconds (10^-6) to milliseconds (10^-3)
    ret /= 1000;

    // Adds the seconds (10^0) after converting them to milliseconds (10^-3)
    ret += (tv.tv_sec * 1000);
#endif
    return ret;
}

void OutputPrimitiveCounters( const dword *pArrayCounters )
{
    message( "Execution count of each primitive:" );

    message( "addrof:   %u", pArrayCounters[p_addrof] );  
    message( "load:     %u", pArrayCounters[p_load] );    
    message( "mov:      %u", pArrayCounters[p_mov] );     
    message( "store:    %u", pArrayCounters[p_store] );   
    message( "drop:     %u", pArrayCounters[p_drop] );    
    message( "dup:      %u", pArrayCounters[p_dup] );     
    message( "swap:     %u", pArrayCounters[p_swap] );    
    message( "or:       %u", pArrayCounters[p_or] );      
    message( "xor:      %u", pArrayCounters[p_xor] );     
    message( "and:      %u", pArrayCounters[p_and] );     
    message( "shl:      %u", pArrayCounters[p_shl] );     
    message( "shr:      %u", pArrayCounters[p_shr] );     
    message( "add:      %u", pArrayCounters[p_add] );     
    message( "sub:      %u", pArrayCounters[p_sub] );     
    message( "mul:      %u", pArrayCounters[p_mul] );     
    message( "div:      %u", pArrayCounters[p_div] );     
    message( "mod:      %u", pArrayCounters[p_mod] );     
    message( "inc:      %u", pArrayCounters[p_inc] );     
    message( "dec:      %u", pArrayCounters[p_dec] );     
    message( "neg:      %u", pArrayCounters[p_neg] );     
    message( "not:      %u", pArrayCounters[p_not] );     
    message( "nz:       %u", pArrayCounters[p_nz] );      
    message( "lor:      %u", pArrayCounters[p_lor] );     
    message( "land:     %u", pArrayCounters[p_land] );    
    message( "lnot:     %u", pArrayCounters[p_lnot] );    
    message( "cmpeq:    %u", pArrayCounters[p_cmpeq] );   
    message( "cmpneq:   %u", pArrayCounters[p_cmpneq] );  
    message( "cmpgt:    %u", pArrayCounters[p_cmpgt] );   
    message( "cmplt:    %u", pArrayCounters[p_cmplt] );   
    message( "cmpge:    %u", pArrayCounters[p_cmpge] );   
    message( "cmple:    %u", pArrayCounters[p_cmple] );   
    message( "convert:  %u", pArrayCounters[p_convert] ); 
    message( "goto:     %u", pArrayCounters[p_goto] );    
    message( "ifz:      %u", pArrayCounters[p_ifz] );     
    message( "ifnz:     %u", pArrayCounters[p_ifnz] );    
    message( "ifeq:     %u", pArrayCounters[p_ifeq] );    
    message( "ifneq:    %u", pArrayCounters[p_ifneq] );   
    message( "ifleq:    %u", pArrayCounters[p_ifleq] );   
    message( "ifgeq:    %u", pArrayCounters[p_ifgeq] );   
    message( "ifg:      %u", pArrayCounters[p_ifg] );     
    message( "ifl:      %u", pArrayCounters[p_ifl] );     
    message( "calli:    %u", pArrayCounters[p_calli] );   
    message( "call:     %u", pArrayCounters[p_call] );    
    message( "ret:      %u", pArrayCounters[p_ret] );     
    message( "new:      %u", pArrayCounters[p_new] );     
    message( "get:      %u", pArrayCounters[p_get] );     
    message( "put:      %u", pArrayCounters[p_put] );     
    message( "invoke:   %u", pArrayCounters[p_invoke] );  
    message( "gettype:  %u", pArrayCounters[p_gettype] ); 
    message( "enter:    %u", pArrayCounters[p_enter] );   
    message( "leave:    %u", pArrayCounters[p_leave] );   
    message( "nop:      %u", pArrayCounters[p_nop] );     
    message( "dbgbreak: %u", pArrayCounters[p_dbgbreak] );
    message( "pack:     %u", pArrayCounters[p_pack] );    
    message( "upack:    %u", pArrayCounters[p_upack] );   
    message( "stkvar:   %u", pArrayCounters[p_stkvar] );
    message( "label:    %u", pArrayCounters[p_label] );   
}

void Run( const char *lpszFileName, int argc, char *argv[] )
{
    SYMBOL_LIST  listGlobal;
    STRING_ARRAY litPool;
    string csFName = lpszFileName;

    if( XORT_OK == LoadExecutable( csFName, listGlobal, litPool ) )
    {
        csFName += ".log";

        io_logOpen( csFName );
        xInitPrims();

        SymbolRef mainEntry = FindSymbol( listGlobal, "main", TRUE );
        if( mainEntry.isNULL() )
        {
            message( "entry point not found" );
        } else { // execute file!

            int iRet = XORT_OK;

            try {
                XRuntime runTime;
                if( ( iRet = runTime.Initialize( listGlobal, litPool, NULL ) ) == XORT_OK )
                {
                    litPool.clear(); // no more needed

                    qword qwStartTime = GetTimeMs64();

                    iRet = runTime.Run( mainEntry, argc-1, argv+1 ); // hide XVM path :)

                    qword qwEndTime = GetTimeMs64();

                    if( iRet != XORT_OK ) {
                        message( "runtime error: %d", iRet );
                    } else {
                        message( "Executed primitives : %u", runTime.get_ExecutedPrimsCount() );
                        message( "Max stack allocated : %u", runTime.get_MaxStackAllocatedDepth() );
                        message( "Max recursion depth : %u", runTime.get_MaxRecursionDepth() );
                        message( "Execution time      : %llu ms", qwEndTime - qwStartTime  );
                        OutputPrimitiveCounters( runTime.get_PrimitivesCounters() );
                    }
                }
                else {
                    message( "runtime initialization error: %d", iRet );
                }
            }
            catch ( int iError ) {
                message( "runtime exception: %d", iError );
            }
        }
        
        xFreePrims();
        io_logClose();
    } else {
        printf( "can not load file: '%s'\r\n", lpszFileName );
    }
}

int main( int argc, char *argv[] )
{
    printf( "Xort Virtual Machine, version 1.0\n" );
    printf( "Copyright (c) 2014, Vladimir Gumenuk\n" );

    if( argc < 2 )
    {
        printf( "target xort executable not specified\n" );
        return 0;
    }

#ifdef BUILD_FOR_PC
    _CrtMemState s1;
    _CrtMemCheckpoint( &s1 );
#endif
    
    Run( argv[1], argc, argv );

#ifdef BUILD_FOR_PC
    _CrtSetDumpClient( &DumpClient );
    _CrtSetReportHook( &CrtReportHook );
    _CrtMemDumpAllObjectsSince( &s1 );
#endif
    
    return 0;
}
